package utils.query;

import play.db.jpa.GenericModel;

import javax.persistence.*;
import java.lang.reflect.Field;
import java.util.*;

public class FieldType {
	public static Map<String, Class> buildFieldTypes(Class clazz, String alias,
			Set<Class> builded) {

		Map<String, Class> maps = new HashMap<String, Class>();
		
		Class clacc = clazz; 
		
		Set<Field> fields = new HashSet<Field>();
		while (!clazz.equals(GenericModel.class)) {
			Collections.addAll(fields, clazz.getDeclaredFields());
			clazz = clazz.getSuperclass();
		}

		for (Field field : fields) {
			if (field.isAnnotationPresent(OneToMany.class)
					|| field.isAnnotationPresent(ManyToMany.class)) {
				continue;
			}
			Class<?> fieldType = field.getType();
			if (field.isAnnotationPresent(OneToOne.class)
					|| field.isAnnotationPresent(ManyToOne.class)) {
				maps.put(alias + "." + field.getName(), fieldType);
				if (!builded.contains(clacc)) {
					Set<Class> nextBuilded = new HashSet<Class>();
					nextBuilded.addAll(builded);
					nextBuilded.add(clacc);
					maps.putAll(buildFieldTypes(fieldType, alias + "."
							+ field.getName(), nextBuilded));
				}
				continue;
			}
			if (field.isAnnotationPresent(Temporal.class)) {
				Temporal annotation = field.getAnnotation(Temporal.class);
				switch (annotation.value()) {
				case DATE:
					maps.put(alias + "." + field.getName(), java.sql.Date.class);
					break;
				case TIME:
					maps.put(alias + "." + field.getName(), java.sql.Time.class);
					break;
				case TIMESTAMP:
					maps.put(alias + "." + field.getName(),
							java.sql.Timestamp.class);
					break;
				}
				continue;
			}

			// deal with int.class,float.class,long.class,etc
			if(isPrimitiveNumberClass(fieldType)) { 
				maps.put(alias + "." + field.getName(), getWrapperClass(fieldType));
				continue; 
			}
			
			maps.put(alias + "." + field.getName(), fieldType);
		}
		return maps;
	}

	public static boolean isPrimitiveNumberClass(Class clazz){
		for (Class clacc : PRIMITIVE_NUMBER_CLASSES) {
			if(clacc.equals(clazz))
				return true;
		}
		return false;
	}
	public static final Class[] PRIMITIVE_NUMBER_CLASSES = new Class[] {
			int.class,
			double.class,
			float.class,
			long.class,
	};

	public static Class getWrapperClass(Class clazz){
		if(int.class.equals(clazz)) return Integer.class;
		if(double.class.equals(clazz)) return Double.class;
		if(float.class.equals(clazz)) return Float.class;
		if(long.class.equals(clazz)) return Long.class;
		return null;
	}

}
